<template>
   <div>
       <div id="container" ref="container"></div>
   </div>
</template>

<script lang='ts'>
    import { computed, getCurrentInstance, ref, watch,onMounted,defineComponent, toRaw } from 'vue';
    import { templateRef } from '@vueuse/core';
    let $THREE = window.THREE;
    export default defineComponent({
        props:{},
        setup(props,context){
            let {ctx}:any = getCurrentInstance();

                const container = templateRef<any>('container');
                const camera = ref<any>(null);
                const renderer = ref<any>(null);
                const rollOverMesh = ref<any>(null);
                const scene = ref<any>(new $THREE.Scene());
                const directionalLight = ref<any>();
                const pointLight = ref<any>(); 
                const ambientLight = ref<any>();
                const rollOverMaterial = ref<any>();
                const cubeGeo = ref<any>();
                const cubeMaterial = ref<any>();
                const raycaster = ref<any>(new $THREE.Raycaster());
                const pointer = ref<any>();
                const plane = ref<any>();
                const objects = ref<any>([]);
                let isShiftDown = false;
                scene.value.background = new $THREE.Color( 0xf0f0f0 );
                const init = ()=>{
                    initCamera();
                    initLights();
                    initContent();
                    initRenderer();
                    ctx.$nextTick(()=>{
                        if(raycaster.value&&raycaster.value.intersectObjects){
                            document.addEventListener( 'pointermove',onPointerMove);
                            document.addEventListener( 'pointerdown', onPointerDown );
                            document.addEventListener( 'keydown', onDocumentKeyDown );
                            document.addEventListener( 'keyup', onDocumentKeyUp );
                        }
                    })
                    let controls = new $THREE.OrbitControls(camera.value, renderer.value.domElement);
                    loop();
                }

                const initCamera = ()=>{
                    camera.value = new $THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
                    camera.value.position.set(500,800,1300);
                    camera.value.lookAt(0,0,0);
                }

                const initLights =()=>{
                    const ambientLight = new $THREE.AmbientLight( 0x606060 );
				    scene.value.add(ambientLight);
                    const directionalLight = new $THREE.DirectionalLight( 0xffffff );
                    directionalLight.position.set( 1, 0.75, 0.5 ).normalize();
                    scene.value.add(directionalLight);
                }

                const initRenderer =()=>{
                    renderer.value = new $THREE.WebGLRenderer({antialias:true});
                    renderer.value.setPixelRatio(window.devicePixelRatio);
                    renderer.value.setSize(window.innerWidth,window.innerHeight);
                    container.value.appendChild(renderer.value.domElement);
                }

                const initContent =()=>{
                    // roll-over helpers
                    let rollOverGeo = new $THREE.BoxGeometry( 50, 50, 50 );
                    rollOverMaterial.value = new $THREE.MeshBasicMaterial( { color: 0xff0000, opacity: 0.5, transparent: true } );
                    rollOverMesh.value = new $THREE.Mesh( rollOverGeo, rollOverMaterial.value );
                    scene.value.add(rollOverMesh.value);
                    // cubes
                    cubeGeo.value = new $THREE.BoxGeometry( 50, 50, 50 );
                    cubeMaterial.value = new $THREE.MeshLambertMaterial( { color: 0xfeb74c, map: new $THREE.TextureLoader().load( '/square-outline-textured.png' ) } );
                    // grid
                    let gridHelper = new $THREE.GridHelper( 1000, 20 );
                    scene.value.add(gridHelper);
                    pointer.value = new $THREE.Vector2();
                    let geometry = new $THREE.PlaneGeometry( 1000, 1000 );
                    geometry.rotateX( - Math.PI / 2 );
                    plane.value = new $THREE.Mesh(toRaw(geometry),toRaw(new $THREE.MeshBasicMaterial( { visible: false } )) );
                    scene.value.add(toRaw(plane.value));
                    objects.value.push(toRaw(plane.value));
                }

                
			const onPointerMove =(event:any)=> {
				pointer.value.set((event.clientX/window.innerWidth) * 2 - 1, - (event.clientY/window.innerHeight) * 2 + 1);
				raycaster.value.setFromCamera(toRaw(pointer.value),toRaw(camera.value));
				let intersects = raycaster.value.intersectObjects(toRaw(objects.value));
				if (intersects.length > 0) {
					const intersect = intersects[ 0 ];
					rollOverMesh.value.position.copy( intersect.point ).add( intersect.face.normal );
					rollOverMesh.value.position.divideScalar( 50 ).floor().multiplyScalar( 50 ).addScalar( 25 );
				}
				render();
			}

			const onPointerDown =(event:any)=>{
				pointer.value.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1 );
				raycaster.value.setFromCamera(toRaw(pointer.value), toRaw(camera.value) );
				let intersects = raycaster.value.intersectObjects( objects.value );
				if ( intersects.length > 0 ) {
					const intersect = intersects[0];
					// delete cube
					if (isShiftDown) {
						if (intersect.object !== plane) {
							scene.remove(toRaw(intersect.object));
							objects.value.splice( objects.value.indexOf( intersect.object ), 1 );
						}
					} else {
						const voxel = new $THREE.Mesh(cubeGeo.value,cubeMaterial.value);
						voxel.position.copy( intersect.point ).add( intersect.face.normal );
						voxel.position.divideScalar( 50 ).floor().multiplyScalar( 50 ).addScalar( 25 );
	    				scene.value.add(voxel);
						objects.value.push(voxel);
					}
					render();
				}
			}

			const onDocumentKeyDown=(event:any)=> {
				switch ( event.keyCode ) {
					case 16: isShiftDown = true; break;
                    }
			}

			const onDocumentKeyUp =(event:any)=>{
				switch ( event.keyCode ) {
		    		case 16: isShiftDown = false; break;
				}
			}

            const render =()=>{
                renderer.value.render(toRaw(scene.value), camera.value);
            }

            const loop =()=>{
                requestAnimationFrame(loop);
                renderer.value.render(toRaw(scene.value), camera.value);
            }

            onMounted(()=>{
                init();

            })
            return{
            }
        },
        components:{
        }
    })
</script>

<style scoped>
</style>
